﻿using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using GraphDrawing.Interface;
using GraphDrawing.View;

namespace GraphDrawing
{
    public class DragDropController : IDragSourceController, IDropTargetController
    {
        readonly IDragView view;

        readonly IDropCallback callback;

        /// <summary>
        /// Состояние перетаскивания.
        /// </summary>
        State state = State.NONE;

        /// <summary>
        /// Флаг, равный true, если в данный момент выполняется перетаскивание над объектом назначения, иначе false.
        /// </summary>
        bool isDraggingOnTarget = false;

        public DragDropController(IDragView view, IDropCallback callback)
        {
            this.view = view;
            this.callback = callback;
        }

        public void RequestBeginDrag(object item)
        {
            if (state != State.NONE)
                throw new InvalidOperationException("invalid state to begin dragging (" + state + ")");
            state = State.INITIATED;
            view.BeginDrag(item, DragDropEffects.Copy);
        }

        void IDragSourceController.OnLeave()
        {
            //Означает, что операция перетаскивание прерывается в границах контрола.
            Debug.WriteLine("Source.OnLeave; State={0}", state);
            if (state == State.NONE)
                return;
            state = State.DRAGGING;
        }

        public DragAction CheckDraggingState(bool escapePressed, bool leftButtonPressed)
        {
            Debug.WriteLine("Source.CheckDraggingState: ESC={0}; LB={1}", escapePressed, leftButtonPressed);
            if (state == State.NONE)
                throw new InvalidOperationException("invalid state to continue dragging (" + state + ")");
            if (!leftButtonPressed && isDraggingOnTarget)
                return DragAction.Drop;
            if (leftButtonPressed && !escapePressed && state != State.INVALID)
                return DragAction.Continue;
            Reset();
            return DragAction.Cancel;
        }

        public bool CheckCanAcceptDrop(IDataObject data, DragDropEffects sourceEffect)
        {
            Debug.WriteLine("Source.CheckCanAcceptDrop");
            isDraggingOnTarget = sourceEffect.HasFlag(DragDropEffects.Copy);
            return isDraggingOnTarget;
        }

        void IDropTargetController.OnLeave()
        {
            Debug.WriteLine("Target.OnLeave");
            if (state != State.DRAGGING)
                throw new InvalidOperationException("invalid state to leave target control (" + state + ")");
            isDraggingOnTarget = false;
        }

        public void OnProcessDragging(IDataObject data, Point point)
        {
            Debug.WriteLine("Target.OnProcessDragging");
            if (state != State.DRAGGING)
                throw new InvalidOperationException("invalid state to complete drop (" + state + "");
        }

        public void CompleteDrop(IDataObject data, Point point)
        {
            Debug.WriteLine("Target.CompleteDrop");
            Reset();
            callback.OnDrop(data, point);
        }

        /// <summary>
        /// Выполняет сброс состояния и очищает ресурсы цели перетаскивания.
        /// </summary>
        void Reset()
        {
            state = State.NONE;
            isDraggingOnTarget = false;
        }

        /// <summary>
        /// Состояние процесса перетаскивания.
        /// </summary>
        enum State
        {
            /// <summary>
            /// Перетаскивание не выполняется.
            /// </summary>
            NONE,

            /// <summary>
            /// Перетаскивание инициировано, но курсор не покинул границы контрола.
            /// </summary>
            INITIATED,

            /// <summary>
            /// Выполняется перетаскивание за пределами источника.
            /// </summary>
            DRAGGING,

            /// <summary>
            /// Возникла ошибка обработки во время перетаскивания. При запросе состояния необходимо отменить передачу.
            /// </summary>
            INVALID
        }
    }
}